#include "SimulatedAnnealing.h"
#include <ctime>
#include <omp.h>
#include "pfs_calculator.h"

SA_PFS::SA_PFS(const PFS_Item& pfs_item, double time_limit,
			double start_temp, double cool_rate, double end_temp, int iterate_len)
	: PFS_Calculator(pfs_item, time_limit), kStartTemp(start_temp), 
		kCoolRate(cool_rate), kFinalTemperature(end_temp),
		kIterateLength(iterate_len) {}

PFS_Calculator::Answer SA_PFS::Calculate() {
	InitResult();
	
	double start_time = omp_get_wtime();
	#pragma omp parallel
	{
		SA_process(start_time);
	}
	return best_answer();
}


void SA_PFS::SA_process(const double start_time) {
	RandomGenReal<double> randomDouble(0.0 , 1.0);
	double high_temperature = kStartTemp;
	double coolRate = kCoolRate;
	const double kCoolRateMax = 0.9998;
	const int kMaxNumJump = 2;
	

	Solution solution = GetRandomSolution();
	int scoreNow = Score(solution);
	PushAnswer(scoreNow, solution);	
	
	while(true) {
		double temperature = high_temperature;
		while(temperature > kFinalTemperature) {
			int jump_counter = 0;

			for (int i = 0 ; i < kIterateLength  ; ++i) {
				if (omp_get_wtime() - start_time > time_limit()) return;
				if (jump_counter >= kMaxNumJump) break;

				Solution neighbor = GetNeighbor(solution);
				int neighborScore = Score(neighbor);
				double doorsill = randomDouble();
				if (neighborScore <= scoreNow ||
						exp((scoreNow - neighborScore) / temperature) > doorsill) {
				
							solution = neighbor;
							scoreNow = neighborScore;
							PushAnswer(neighborScore, neighbor);
							++jump_counter;
				}
			}
			temperature *= coolRate;
		}
		coolRate += (1.0 - coolRate) / 2.0;
		coolRate = std::min(kCoolRateMax, coolRate);
	}
}
